﻿namespace AutoFakes
{
    using System;
    using System.Collections.Generic;

    public static class LinqExtensions
    {
        public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
                                                   Func<TSource, TKey> selector)
        {
            return source.MaxBy(selector, Comparer<TKey>.Default);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization",
            "CA1303:Do not pass literals as localized parameters",
            MessageId = "AutoFakes.LinqExtensions.ThrowIfNull(System.Object,System.String)",
            Justification = "Resource file would be WAY overkill in this scenario and makes the code very ugly")]
        public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
                                                   Func<TSource, TKey> selector, IComparer<TKey> comparer)
        {
            source.ThrowIfNull("source");
            selector.ThrowIfNull("selector");
            comparer.ThrowIfNull("comparer");
            using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
            {
                if (!sourceIterator.MoveNext())
                {
                    throw new InvalidOperationException("Sequence was empty");
                }
                TSource max = sourceIterator.Current;
                TKey maxKey = selector(max);
                while (sourceIterator.MoveNext())
                {
                    TSource candidate = sourceIterator.Current;
                    TKey candidateProjected = selector(candidate);
                    if (comparer.Compare(candidateProjected, maxKey) <= 0) continue;
                    max = candidate;
                    maxKey = candidateProjected;
                }
                return max;
            }
        }

        public static void ThrowIfNull(this object referenceToCheck, string message)
        {
            if (referenceToCheck == null)
            {
                throw new ArgumentNullException(message + " Should not be null");
            }
        }
    }
}